package com.gitee.wsl.jvm.task.thread.executor

import com.gitee.wsl.jvm.task.thread.Scheduler
import com.gitee.wsl.jvm.task.thread.Wrapper
import java.util.LinkedList

/**
 * Tasks with same tag executes serially.
 * Tasks in different tag executes concurrently.
 *
 *
 * For we have remote file cache (see [Downloader.sourceCache],
 * we could use URL as tag to avoid more than one task downloading same remote files
 * at almost same time (May have problem).
 * Because of tasks with same URL executing serially , after the first task download finish,
 * the later tasks with same URL can reuse the cache.
 *
 *
 * Similarly, we have result image cache (see [LruCache],
 * we could use the cache key as tag to avoid decoding duplicate bitmaps.
 */
class TagExecutor {
    @Synchronized
    fun execute(tag: String, r: Runnable) {
        if (!scheduledTags.contains(tag)) {
            start(tag, r)
        } else {
            var queue = waitingQueues[tag]
            if (queue == null) {
                queue = LinkedList()
                waitingQueues[tag] = queue
            }
            queue.offer(r)
        }
    }

    private fun start(tag: String, r: Runnable) {
        scheduledTags.add(tag)
        Scheduler.pipeExecutor.execute(object : Wrapper(r) {
            override fun run() {
                try {
                    task.run()
                } finally {
                    scheduleNext(tag)
                }
            }
        })
    }

    @Synchronized
    private fun scheduleNext(tag: String) {
        scheduledTags.remove(tag)
        val queue = waitingQueues[tag]
        if (queue != null) {
            val r = queue.poll()
            r?.let { start(tag, it) } ?: waitingQueues.remove(tag)
        }
    }

    companion object {
        private val scheduledTags: MutableSet<String> = HashSet<String>()
        private val waitingQueues: MutableMap<String, LinkedList<Runnable>> = HashMap<String, LinkedList<Runnable>>()
    }
}
